Lær hvordan du bruger JavaScripts AbortController til effektivt at annullere asynkrone operationer som fetch-forespørgsler, timere og mere, hvilket sikrer renere og mere performant kode.
JavaScript AbortController: Mastering annullering af asynkrone operationer
I moderne webudvikling er asynkrone operationer allestedsnærværende. Hentning af data fra API'er, indstilling af timere og håndtering af brugerinteraktioner involverer ofte kode, der kører uafhængigt og potentielt i en længere periode. Der er dog scenarier, hvor du skal annullere disse operationer, før de er færdige. Det er her, AbortController
-interfacet i JavaScript kommer til undsætning. Det giver en ren og effektiv måde at signalere annulleringsanmodninger til DOM-operationer og andre asynkrone opgaver.
Forståelse af behovet for annullering
Før vi dykker ned i de tekniske detaljer, skal vi forstå, hvorfor annullering af asynkrone operationer er vigtigt. Overvej disse almindelige scenarier:
- Brugernavigation: En bruger initierer en søgeforespørgsel, der udløser en API-forespørgsel. Hvis de hurtigt navigerer til en anden side, før anmodningen er færdig, bliver den oprindelige anmodning irrelevant og skal annulleres for at undgå unødvendig netværkstrafik og potentielle bivirkninger.
- Timeout-styring: Du indstiller en timeout for en asynkron operation. Hvis operationen er færdig, før timeouten udløber, skal du annullere timeouten for at forhindre redundant kodeudførelse.
- Component Unmounting: I front-end-frameworks som React eller Vue.js foretager komponenter ofte asynkrone anmodninger. Når en komponent unmountes, skal alle igangværende anmodninger, der er knyttet til den pågældende komponent, annulleres for at undgå hukommelseslækager og fejl forårsaget af opdatering af umonterede komponenter.
- Ressourcebegrænsninger: I ressourcebegrænsede miljøer (f.eks. mobile enheder, indlejrede systemer) kan annullering af unødvendige operationer frigøre værdifulde ressourcer og forbedre ydeevnen. For eksempel annullering af en stor billeddownload, hvis brugeren ruller forbi den pågældende del af siden.
Introduktion til AbortController og AbortSignal
AbortController
-interfacet er designet til at løse problemet med annullering af asynkrone operationer. Det består af to nøglekomponenter:
- AbortController: Dette objekt administrerer annulleringssignalet. Det har en enkelt metode,
abort()
, som bruges til at signalere en annulleringsanmodning. - AbortSignal: Dette objekt repræsenterer det signal, at en operation skal afbrydes. Det er knyttet til en
AbortController
og videregives til den asynkrone operation, der skal kunne annulleres.
Grundlæggende brug: Annullering af Fetch-forespørgsler
Lad os starte med et simpelt eksempel på at annullere en fetch
-forespørgsel:
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});
// To cancel the fetch request:
controller.abort();
Forklaring:
- Vi opretter en
AbortController
-instans. - Vi henter det tilknyttede
AbortSignal
fracontroller
. - Vi sender
signal
tilfetch
-valgmulighederne. - Hvis vi skal annullere anmodningen, kalder vi
controller.abort()
. - I
.catch()
-blokken kontrollerer vi, om fejlen er enAbortError
. Hvis det er tilfældet, ved vi, at anmodningen blev annulleret.
Håndtering af AbortError
Når controller.abort()
kaldes, afvises fetch
-anmodningen med en AbortError
. Det er afgørende at håndtere denne fejl korrekt i din kode. Hvis du undlader at gøre det, kan det føre til uhåndterede løftede afvisninger og uventet adfærd.
Her er et mere robust eksempel med fejlhåndtering:
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
return null; // Or throw the error to be handled further up
} else {
console.error('Fetch error:', error);
throw error; // Re-throw the error to be handled further up
}
}
}
fetchData();
// To cancel the fetch request:
controller.abort();
Bedste praksis for håndtering af AbortError:
- Kontroller fejlnavnet: Kontroller altid, om
error.name === 'AbortError'
for at sikre, at du håndterer den korrekte fejltype. - Returner en standardværdi eller kast igen: Afhængigt af din applikations logik, kan du muligvis returnere en standardværdi (f.eks.
null
) eller kaste fejlen igen for at blive håndteret længere oppe i kaldestakken. - Ryd ressourcer: Hvis den asynkrone operation allokerede ressourcer (f.eks. timere, event listeners), skal du rydde dem i
AbortError
-handleren.
Annullering af timere med AbortSignal
AbortSignal
kan også bruges til at annullere timere, der er oprettet med setTimeout
eller setInterval
. Dette kræver lidt mere manuelt arbejde, da de indbyggede timerfunktioner ikke direkte understøtter AbortSignal
. Du skal oprette en brugerdefineret funktion, der lytter efter abortsignalet og rydder timeren, når den udløses.
function cancellableTimeout(callback, delay, signal) {
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
resolve(callback());
}, delay);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Timeout Aborted'));
});
});
return timeoutPromise;
}
const controller = new AbortController();
const signal = controller.signal;
cancellableTimeout(() => {
console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));
// To cancel the timeout:
controller.abort();
Forklaring:
cancellableTimeout
-funktionen tager en callback, en forsinkelse og etAbortSignal
som argumenter.- Den opsætter en
setTimeout
og gemmer timeout-id'et. - Den tilføjer en event listener til
AbortSignal
, der lytter efterabort
-eventen. - Når
abort
-eventen udløses, rydder event listeneren timeouten og afviser løftet.
Annullering af Event Listeners
På samme måde som med timere kan du bruge AbortSignal
til at annullere event listeners. Dette er især nyttigt, når du vil fjerne event listeners, der er knyttet til en komponent, der er ved at blive umonteret.
const controller = new AbortController();
const signal = controller.signal;
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
}, { signal });
// To cancel the event listener:
controller.abort();
Forklaring:
- Vi sender
signal
som en indstilling tiladdEventListener
-metoden. - Når
controller.abort()
kaldes, fjernes event listeneren automatisk.
AbortController i React-komponenter
I React kan du bruge AbortController
til at annullere asynkrone operationer, når en komponent unmountes. Dette er afgørende for at forhindre hukommelseslækager og fejl forårsaget af opdatering af umonterede komponenter. Her er et eksempel ved hjælp af useEffect
-hooket:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
}
}
fetchData();
return () => {
controller.abort(); // Annuller fetch-anmodningen, når komponenten unmountes
};
}, []); // Empty dependency array ensures this effect runs only once on mount
return (
{data ? (
Data: {JSON.stringify(data)}
) : (
Loading...
)}
);
}
export default MyComponent;
Forklaring:
- Vi opretter en
AbortController
iuseEffect
-hooket. - Vi sender
signal
tilfetch
-anmodningen. - Vi returnerer en oprydningsfunktion fra
useEffect
-hooket. Denne funktion kaldes, når komponenten unmountes. - Inde i oprydningsfunktionen kalder vi
controller.abort()
for at annullere fetch-anmodningen.
Avancerede brugsscenarier
Kædning af AbortSignals
Nogle gange vil du muligvis kæde flere AbortSignal
sammen. Du kan f.eks. have en overordnet komponent, der skal annullere operationer i sine underordnede komponenter. Du kan opnå dette ved at oprette en ny AbortController
og sende dets signal til både de overordnede og underordnede komponenter.
Brug af AbortController med tredjepartsbiblioteker
Hvis du bruger et tredjepartsbibliotek, der ikke direkte understøtter AbortSignal
, skal du muligvis tilpasse din kode, så den fungerer med bibliotekets annulleringsmekanisme. Dette kan involvere at indpakke bibliotekets asynkrone funktioner i dine egne funktioner, der håndterer AbortSignal
.
Fordele ved at bruge AbortController
- Forbedret ydeevne: Annullering af unødvendige operationer kan reducere netværkstrafik, CPU-forbrug og hukommelsesforbrug, hvilket fører til forbedret ydeevne, især på ressourcebegrænsede enheder.
- Renere kode:
AbortController
giver en standardiseret og elegant måde at administrere annullering på, hvilket gør din kode mere læsbar og vedligeholdelsesvenlig. - Forebyggelse af hukommelseslækager: Annullering af asynkrone operationer, der er knyttet til umonterede komponenter, forhindrer hukommelseslækager og fejl forårsaget af opdatering af umonterede komponenter.
- Bedre brugeroplevelse: Annullering af irrelevante anmodninger kan forbedre brugeroplevelsen ved at forhindre forældet information i at blive vist og reducere opfattet latenstid.
Browserkompatibilitet
AbortController
understøttes bredt i moderne browsere, herunder Chrome, Firefox, Safari og Edge. Du kan tjekke kompatibilitetstabellen på MDN Web Docs for de seneste oplysninger.
Polyfills
For ældre browsere, der ikke oprindeligt understøtter AbortController
, kan du bruge en polyfill. En polyfill er et stykke kode, der giver funktionaliteten af en nyere funktion i ældre browsere. Der er flere AbortController
polyfills tilgængelige online.
Konklusion
AbortController
-interfacet er et kraftfuldt værktøj til at administrere asynkrone operationer i JavaScript. Ved at bruge AbortController
kan du skrive renere, mere performant og mere robust kode, der håndterer annullering på en elegant måde. Uanset om du henter data fra API'er, indstiller timere eller administrerer event listeners, kan AbortController
hjælpe dig med at forbedre den overordnede kvalitet af dine webapplikationer.